'use strict';

const crypto = require('crypto');
const WebSocket = require('ws');
const ffmpeg = require('fluent-ffmpeg');
const ffmpegStaticPath = require('ffmpeg-static');
const fs = require('fs');

function resolveAndSetFfmpegPath() {
  const candidates = [
    process.env.FFMPEG_PATH,
    ffmpegStaticPath,
    '/usr/bin/ffmpeg',
    '/usr/local/bin/ffmpeg'
  ].filter(Boolean);
  let chosen = null;
  for (const p of candidates) {
    try {
      if (fs.existsSync(p)) { chosen = p; break; }
    } catch(_) {}
  }
  if (!chosen) {
    console.error('未找到可用的 ffmpeg 可执行文件，候选路径:', candidates);
    throw new Error('FFmpeg binary not found');
  }
  try { fs.chmodSync(chosen, 0o755); } catch(_) {}
  ffmpeg.setFfmpegPath(chosen);
  console.log('使用 ffmpeg 路径:', chosen);
}

// 讯飞API配置
const APPID = 'e88f69c4';
const APISecret = 'YTZmZWY2YjI2YmY1MGY0ODI3NzAxNjIx';
const APIKey = '6d69c7c1321c3b550ca6d8d6a8c91e03';

// 科大讯飞接口配置 - WebSocket v2
const config = {
  wssUrl: 'wss://iat-api.xfyun.cn/v2/iat',
  host: 'iat-api.xfyun.cn',
  appid: APPID,
  apiSecret: APISecret,
  apiKey: APIKey,
  uri: '/v2/iat'
};

// 构造 v1 HTTP API 头部（X-Param/X-CheckSum 模式）
// 生成 WebSocket 鉴权参数（v2）
function buildWsAuth() {
  const date = new Date().toUTCString();
  const signatureOrigin = `host: ${config.host}\ndate: ${date}\nGET ${config.uri} HTTP/1.1`;
  const signatureSha = crypto.createHmac('sha256', config.apiSecret).update(signatureOrigin).digest('base64');
  const authorizationOrigin = `api_key=\"${config.apiKey}\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"${signatureSha}\"`;
  const authorization = Buffer.from(authorizationOrigin).toString('base64');
  const url = `${config.wssUrl}?authorization=${encodeURIComponent(authorization)}&date=${encodeURIComponent(date)}&host=${encodeURIComponent(config.host)}`;
  return { url, date, authorization };
}

// 通过 WebSocket 调用讯飞 iat v2
async function iatByWebSocket(audioBase64, audioFormat = 'mp3') {
  return new Promise((resolve, reject) => {
    const auth = buildWsAuth();
    const ws = new WebSocket(auth.url);

    // 使用讯飞 WPGS 增量结果重组：基于 sn/pgs/rg 去重与替换
    // 参考：当 pgs === 'rpl' 时，删除 rg 范围内已有分片，再用当前 sn 的内容替换
    let resultText = '';
    const resultMap = new Map(); // sn -> text 片段
    let closed = false;
    let sentFinal = false;

    // 将 base64 分片（长度为 8192 的倍数，且保证 4 的倍数，避免破坏 base64 分组）
    function splitBase64(b64, chunkChars = 4096) {
      // 约 ~3KB/片（4096 字符，4 的倍数），减少帧数以降低整体发送时延
      const size = Math.max(1024, chunkChars - (chunkChars % 4));
      const chunks = [];
      for (let i = 0; i < b64.length; i += size) {
        chunks.push(b64.slice(i, i + size));
      }
      return chunks;
    }

    function frameData(status, audio) {
      return {
        data: {
          status,
          format: audioFormat === 'mp3' ? 'audio/mp3' : 'audio/L16;rate=16000',
          encoding: audioFormat === 'mp3' ? 'lame' : 'raw',
          audio
        }
      };
    }

    ws.on('open', () => {
      // 首帧（包含业务与第一段音频）
      const chunks = splitBase64(audioBase64);
      const firstAudio = chunks.shift() || '';
      const frame0 = {
        common: { app_id: config.appid },
        business: {
          language: 'zh_cn',
          domain: 'iat',
          accent: 'mandarin',
          vad_eos: 5000,
          dwa: 'wpgs'
        },
        data: {
          status: chunks.length > 0 ? 0 : 2,
          format: audioFormat === 'mp3' ? 'audio/mp3' : 'audio/L16;rate=16000',
          encoding: audioFormat === 'mp3' ? 'lame' : 'raw',
          audio: firstAudio
        }
      };
      ws.send(JSON.stringify(frame0));

      if (chunks.length === 0) {
        sentFinal = true;
        return;
      }

      // 以较短时间片节流发送，尽快完成所有帧传输
      let idx = 0;
      const timer = setInterval(() => {
        if (idx < chunks.length - 1 && ws.readyState === WebSocket.OPEN) {
          ws.send(JSON.stringify(frameData(1, chunks[idx])));
          idx++;
        } else if (idx === chunks.length - 1 && ws.readyState === WebSocket.OPEN) {
          ws.send(JSON.stringify(frameData(2, chunks[idx])));
          clearInterval(timer);
          sentFinal = true;
        } else if (ws.readyState !== WebSocket.OPEN) {
          clearInterval(timer);
        }
      }, 15); // 每 15ms 一片，加快发送速度
    });

    ws.on('message', (msg) => {
      try {
        const data = JSON.parse(msg);
        if (data.code === 0) {
          const r = data && data.data && data.data.result
          if (r && r.ws) {
            const sn = typeof r.sn === 'number' ? r.sn : undefined
            const pgs = r.pgs // 'apd' | 'rpl'（可能不存在）
            const rg = Array.isArray(r.rg) ? r.rg : null // [start, end]
            const part = r.ws.map(item => (item.cw || []).map(cw => cw.w).join('')).join('')

            if (typeof sn === 'number') {
              // 处理替换策略
              if (pgs === 'rpl' && rg && rg.length === 2) {
                const [start, end] = rg
                for (let i = start; i <= end; i++) {
                  resultMap.delete(i)
                }
              }
              resultMap.set(sn, part)

              // 可选：如果需要中途预览，可在此重建 resultText（当前仅最终返回）
            } else {
              // 无 sn 时退化为简单追加
              resultText += part
            }
          }
          // 若服务端提示最终帧（data.data.status === 2），立即关闭连接以减少尾延迟
          if (data.data && typeof data.data.status === 'number' && data.data.status === 2) {
            try { ws.close(); } catch(_) {}
          }
        } else {
          throw new Error(`${data.code}|${data.message || data.desc || 'ws error'}`);
        }
      } catch (e) {
        // 非 JSON 心跳忽略
      }
    });

    // 超时兜底：末帧发送后短时间内若未 close，则主动关闭
    const watchdog = setInterval(() => {
      if (sentFinal && ws.readyState === WebSocket.OPEN) {
        ws.close();
        clearInterval(watchdog);
      }
    }, 800);

    ws.on('close', () => {
      if (!closed) {
        closed = true;
        clearInterval(watchdog);
        // 若使用了 WPGS，则基于 sn 排序重建最终文本
        if (resultMap.size > 0) {
          const ordered = Array.from(resultMap.entries()).sort((a, b) => a[0] - b[0])
          const finalText = ordered.map(([, txt]) => txt).join('')
          resolve(finalText || '')
        } else {
          resolve(resultText || '')
        }
      }
    });

    ws.on('error', (err) => {
      if (!closed) {
        closed = true;
        clearInterval(watchdog);
        reject(err);
      }
    });
  });
}

// 将 webm/ogg 转为 16k 单声道 PCM 裸流（base64）
async function transcodeWebmOggToPcm16(base64Data, container = 'webm') {
  const fs = require('fs');
  const os = require('os');
  const path = require('path');
  const tmpDir = os.tmpdir();
  resolveAndSetFfmpegPath();
  const input = path.join(tmpDir, `in_${Date.now()}.${container}`);
  const output = path.join(tmpDir, `out_${Date.now()}.pcm`);
  await fs.promises.writeFile(input, Buffer.from(base64Data, 'base64'));
  await new Promise((resolve, reject) => {
    ffmpeg(input)
      .noVideo()
      .audioChannels(1)
      .audioFrequency(16000)
      .audioCodec('pcm_s16le')
      .format('s16le')
      .output(output)
      .on('end', resolve)
      .on('error', reject)
      .run();
  });
  const outBuf = await fs.promises.readFile(output);
  try { await fs.promises.unlink(input) } catch(e) {}
  try { await fs.promises.unlink(output) } catch(e) {}
  return outBuf.toString('base64');
}

// 测试讯飞API连接
async function testXunfeiConnection() {
  console.log('=== 开始测试讯飞API连接 ===');
  
  try {
    const auth = buildWsAuth();
    console.log('WS 鉴权 URL:', auth.url);
    
    // 发送一个简单的测试请求
    const testRequestBody = {
      common: {
        app_id: config.appid
      },
      business: {
        language: 'zh_cn',
        domain: 'iat',
        accent: 'mandarin',
        vad_eos: 5000,
        dwa: 'wpgs',
        pd: 'game'
      },
      data: {
        status: 2,
        format: 'audio/mp3',
        encoding: 'lame',
        audio: 'SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjIwLjEwMAAAAAAAAAAAAAAA//tQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAACAAAFbgBtbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1t//////////////////////////////////////////////////////////////////8AAAAATGF2YzU4LjM1AAAAAAAAAAAAAAAAJAYAAAAAAAAABW4A' // 有效的MP3格式测试音频（1秒静音）
      }
    };
    
    console.log('WS 测试将发送末帧音频...');
    const text = await iatByWebSocket(testRequestBody.data.audio, 'mp3');
    console.log('WS 返回合成文本:', text);
    return { success: true, status: 200, data: { text } };
  } catch (error) {
    console.error('讯飞API连接测试失败:', error);
    return {
      success: false,
      error: error.message
    };
  }
}

// 诊断403错误
async function diagnose403Error() {
  console.log('=== 开始诊断403错误 ===');
  
  try {
    const auth = buildWsAuth();
    console.log('WS 鉴权 URL 示例:', auth.url);
    
    // 尝试不同的请求格式
    const testRequests = [
      {
        name: '标准请求',
        body: {
          common: { app_id: config.appid },
          business: {
            language: 'zh_cn',
            domain: 'iat',
            accent: 'mandarin',
            vad_eos: 5000,
            dwa: 'wpgs',
            pd: 'game'
          },
          data: {
            status: 2,
            format: 'audio/mp3',
            encoding: 'lame',
            audio: 'SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjIwLjEwMAAAAAAAAAAAAAAA//tQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAACAAAFbgBtbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1t//////////////////////////////////////////////////////////////////8AAAAATGF2YzU4LjM1AAAAAAAAAAAAAAAAJAYAAAAAAAAABW4A'
          }
        }
      },
      {
        name: '简化请求',
        body: {
          common: { app_id: config.appid },
          business: {
            language: 'zh_cn',
            domain: 'iat',
            accent: 'mandarin'
          },
          data: {
            status: 2,
            format: 'audio/mp3',
            encoding: 'lame',
            audio: 'SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjIwLjEwMAAAAAAAAAAAAAAA//tQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAACAAAFbgBtbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1t//////////////////////////////////////////////////////////////////8AAAAATGF2YzU4LjM1AAAAAAAAAAAAAAAAJAYAAAAAAAAABW4A'
          }
        }
      }
    ];
    
    for (const testReq of testRequests) {
      console.log(`\n=== 测试 ${testReq.name} ===`);
      
      try {
        const text = await iatByWebSocket(testReq.body.data.audio, 'mp3');
        console.log(`${testReq.name} WS 文本:`, text);
        return { success: true, workingRequest: testReq.name, status: 200, data: { text } };
      } catch (error) {
        console.error(`${testReq.name} 失败:`, error.message);
      }
    }
    
    return {
      success: false,
      error: '所有请求都返回403错误'
    };
    
  } catch (error) {
    console.error('诊断过程出错:', error);
    return {
      success: false,
      error: error.message
    };
  }
}

exports.main = async (event, context) => {
  console.log('=== 云函数开始执行 ===')
  console.log('接收到的参数:', event)
  
  try {
    let { audioBase64, audioFormat = 'mp3' } = event;
    
    console.log('云函数收到请求，音频格式:', audioFormat, '数据长度:', audioBase64 ? audioBase64.length : 0);
    console.log('完整接收到的参数:', JSON.stringify(event));
    
    // 如果没有传递任何参数，返回基本信息
    if (!event || Object.keys(event).length === 0) {
      console.log('收到空请求，返回基本信息')
      return {
        code: 0,
        message: '云函数连接正常',
        data: {
          text: '云函数连接测试成功！',
          original: { test: true, message: '空请求测试' }
        }
      };
    }
    
    if (!audioBase64) {
      console.error('缺少音频数据')
      return {
        code: -1,
        message: '缺少音频数据',
        data: null
      };
    }
    
    // 检查是否为测试数据
    if (audioBase64 === 'test_audio_data_base64_string') {
      console.log('检测到测试数据，返回测试响应')
      return {
        code: 0,
        message: '测试连接成功',
        data: {
          text: '云函数连接测试成功！',
          original: { test: true }
        }
      };
    }
    
    // 检查是否为讯飞API连接测试
    if (audioBase64 === 'xunfei_connection_test') {
      console.log('检测到讯飞API连接测试请求')
      const testResult = await testXunfeiConnection();
      
      if (testResult.success) {
        return {
          code: 0,
          message: '讯飞API连接测试成功',
          data: {
            text: '讯飞API连接正常',
            original: testResult
          }
        };
      } else {
        return {
          code: -2,
          message: `讯飞API连接测试失败: ${testResult.error}`,
          data: testResult
        };
      }
    }
    
    // 检查是否为403错误诊断测试
    if (audioBase64 === 'diagnose_403_error') {
      console.log('检测到403错误诊断请求')
      const diagnoseResult = await diagnose403Error();
      
      if (diagnoseResult.success) {
        return {
          code: 0,
          message: '403错误诊断成功',
          data: {
            text: `找到可用请求格式: ${diagnoseResult.workingRequest}`,
            original: diagnoseResult
          }
        };
      } else {
        return {
          code: -2,
          message: `403错误诊断结果: ${diagnoseResult.error}`,
          data: diagnoseResult
        };
      }
    }
    
    // 检查是否为新签名测试（使用 WebSocket v2 直连）
    if (audioBase64 === 'test_new_signature') {
      console.log('检测到新签名测试请求')
      try {
        const sampleMp3 = 'SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjIwLjEwMAAAAAAAAAAAAAAA//tQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAACAAAFbgBtbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1t//////////////////////////////////////////////////////////////////8AAAAATGF2YzU4LjM1AAAAAAAAAAAAAAAAJAYAAAAAAAAABW4A'
        const text = await iatByWebSocket(sampleMp3, 'mp3');
        console.log('WS 新签名测试返回文本:', text)
        return { code: 0, message: '新签名方式测试成功', data: { text } }
      } catch (error) {
        console.error('新签名测试出错:', error)
        return { code: -3, message: `新签名测试异常: ${error.message}` , data: null }
      }
    }
    
    console.log('开始生成讯飞API鉴权信息...')
    // 生成讯飞API请求参数
    const auth = buildWsAuth();
    console.log('WS 开始，url:', auth.url)
    
    // 如是 webm/ogg，则云端转码为 RAW PCM16(16k)
    if (typeof audioBase64 === 'string' && audioBase64.length > 0) {
      // 支持 dataURL 形式，先剥离前缀
      if (audioBase64.startsWith('data:')) {
        const commaIdx = audioBase64.indexOf(',');
        if (commaIdx !== -1) {
          console.log('检测到 dataURL，剥离前缀后再判别容器类型')
          audioBase64 = audioBase64.slice(commaIdx + 1);
        }
      }
      const head4 = audioBase64.slice(0, 4);
      let container = null;
      if (head4 === 'GkXf') container = 'webm';
      else if (head4 === 'T2dn') container = 'ogg';
      else if (audioFormat === 'webm' || audioFormat === 'ogg') container = audioFormat;
      console.log('容器判定: head4=', head4, ' audioFormat=', audioFormat, ' container=', container)
      if (container === 'webm' || container === 'ogg') {
        try {
          console.log('检测到', container, '，开始云端ffmpeg转码为PCM16...')
          const pcmB64 = await transcodeWebmOggToPcm16(audioBase64, container);
          audioBase64 = pcmB64;
          audioFormat = 'raw';
          console.log('webm/ogg 转码完成，长度:', pcmB64.length)
        } catch (e) {
          console.error('云端ffmpeg转码失败:', e)
          return { code: -4, message: `云端转码失败: ${e.message}`, data: null }
        }
      }
    }

    // 构建讯飞API请求体 - 支持MP3或RAW(PCM16)
    const text = await iatByWebSocket(audioBase64, audioFormat);
    console.log('WS 识别完成文本:', text)
    return { code: 0, message: '识别成功', data: { text } }
    
  } catch (error) {
    console.error('云函数执行错误:', error);
    console.error('错误堆栈:', error.stack);
    return {
      code: -3,
      message: `云函数执行错误: ${error.message}`,
      data: null
    };
  }
}; 